package com.xiyu.service.service.customer;

import com.xiyu.service.model.customer.contact.*;
import com.xiyu.service.model.customer.info.*;
import com.xiyu.service.repository.customer.contact.*;
import com.xiyu.service.repository.customer.info.CustomerImageRepository;
import com.xiyu.service.repository.customer.info.CustomerWebsiteRepository;
import com.xiyu.service.vo.customer.customerCheck.CustomerCheckMergeInput;
import com.xiyu.service.model.customer.duplicate.CustomerCheckSetting;
import com.xiyu.service.model.customer.duplicate.CustomerCheckSettingDraft;
import com.xiyu.service.repository.customer.duplicate.CustomerCheckSettingRepository;
import com.xiyu.service.vo.customer.customerCheck.CustomerCheckSettingEditInput;
import com.xiyu.service.vo.customer.customerCheck.CustomerCheckSettingQueryOutput;
import com.xiyu.service.model.system.dept.SystemDept;
import com.xiyu.service.repository.customer.info.CustomerInfoRepository;
import com.xiyu.service.repository.system.user.SystemUserRepository;
import com.xiyu.service.service.system.dept.DeptService;
import com.xiyu.service.service.system.user.UserService;
import com.xiyu.service.vo.customer.customerCheck.CustomerCheckDuplicateSearchOutput;
import com.xiyu.service.vo.customer.customerCheck.CustomerCheckDuplicateSearchInput;
import org.babyfish.jimmer.sql.ast.mutation.SaveMode;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;

import java.util.*;

import org.springframework.transaction.annotation.Transactional;

import java.util.stream.Collectors;

import com.xiyu.service.util.entity.EntityUtils;
import org.babyfish.jimmer.DraftObjects;
import com.xiyu.service.framework.web.web.core.pojo.PageResult;
import com.xiyu.service.convert.customer.CustomerCheckConvert;

import static com.xiyu.service.errorCode.customer.CustomerErrorCode.CUSTOMER_INFO_NOT_EXIST;
import static com.xiyu.service.framework.exception.util.ServiceExceptionUtil.exception;
import static com.xiyu.service.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;

/**
 * 客户查重 Service 实现类
 */
@Service
@Validated
public class CustomerCheckServiceImpl implements CustomerCheckService {

    @Resource
    private CustomerInfoRepository customerInfoRepository;

    @Resource
    private CustomerCheckSettingRepository customerCheckSettingRepository;

    @Resource
    private UserService userService;

    @Resource
    private SystemUserRepository systemUserRepository;

    @Resource
    private CustomerImageRepository customerImageRepository;

    @Resource
    private CustomerWebsiteRepository customerWebsiteRepository;

    @Resource
    private CustomerContactRepository customerContactRepository;

    @Resource
    private CustomerContactMailRepository customerContactMailRepository;

    @Resource
    private CustomerContactBusinessCardRepository customerContactBusinessCardRepository;

    @Resource
    private CustomerContactPhoneRepository customerContactPhoneRepository;

    @Resource
    private CustomerContactSocialMediaRepository customerContactSocialMediaRepository;

    @Resource
    private DeptService deptService;

    final List<String> platformMail =  List.of("hotmail.com",  "139.com",  "tom.com",  "21cn.com",  "yahoo.com",  "sohu.com",  "chinaren.com",  "163.com",
            "126.com",  "yeah.com",  "outlook.com",  "live.com",  "aol.com",  "protonmail.com",  "zoho.com",  "yandex.com",  "yandex.ru",  "gmx.com",  "tutanota.com",
            "hushmail.com",  "mail.com",  "fastmail.com",  "inbox.lv",  "interia.pl",  "libero.it",  "mail.ru",  "my.com",  "onmail.com",  "poczta.fm",  "startmail.com",
            "tuta.io",  "web.de",  "wanadoo.fr",  "yopmail.com",  "zimbra.com",  "mailfence.com",  "mailnesia.com",  "mailinator.com",  "hush.com",  "zohoworkspace.com",
            "icloud.com",  "naver.com",  "daum.net",  "lycos.com",  "seznam.cz",  "china.com",  "rediffmail.com",  "hinet.net",  "nate.com",  "caramail.com",  "hgc.com",
            "o2.pl",  "uol.com.br",  "yandex.ua",  "excite.com",  "zoho.eu",  "t-online.de",  "mail.bg",  "posteo.de",  "gmx.at",  "gmx.co.uk",  "yahoo.co.uk",  "bdlvtu.cn");

    @Override
    public List<CustomerCheckDuplicateSearchOutput> duplicateSearch(CustomerCheckDuplicateSearchInput inputVO) {
        String searchText = inputVO.getSearchText();
        List<Long> customerIds = new ArrayList<>();
        Long userId = getLoginUserId();
        if(Objects.equals(userId, null))
            return null;

        List<CustomerCheckSetting> settings = customerCheckSettingRepository.findAll();
        if(settings.isEmpty())
            return null;
        List<String> settingColumnName = settings.stream().map(CustomerCheckSetting::columnName).collect(Collectors.toList());
        List<Long> ownerUserIds = userService.getOwnerUserIds(userId);
        Long deptId = systemUserRepository.findById(userId).get().deptId();
        List<Long> ownerDeptIds = deptService.getDeptListByParentId(deptId, true).stream().map(SystemDept::id).collect(Collectors.toList());
        boolean isLeader = deptService.isLeaderUser(userId);
        // 查询私海客户
        customerIds.addAll(customerInfoRepository.duplicateCheck(searchText, settingColumnName, false, ownerUserIds,ownerDeptIds, isLeader));

        // 查询公海客户
        customerIds.addAll(customerInfoRepository.duplicateCheck(searchText, settingColumnName, true, ownerUserIds,ownerDeptIds, isLeader));

        // 查询相同后缀邮箱
        if(inputVO.getIsMailSuffix()){
            customerIds.addAll(getSameMailSuffix(searchText, ownerUserIds,ownerDeptIds, isLeader));
        }
        return CustomerCheckConvert.INSTANCE.duplicateSearchOutputConvert(customerInfoRepository.findDetailByIds(customerIds));
    }

    private List<Long> getSameMailSuffix(String searchText, List<Long> ownerUserIds, List<Long> ownerDeptIds, boolean isLeader){
        List<Long> customerIds = new ArrayList<>();
        if(!searchText.contains("@"))
            return customerIds;
        String mailSuffix = searchText.substring(searchText.indexOf("@"));
        if(platformMail.contains(mailSuffix))
            return customerIds;
        customerIds.addAll(customerInfoRepository.duplicateCheckByMailSuffix(mailSuffix, false, ownerUserIds, ownerDeptIds, isLeader));
        customerIds.addAll(customerInfoRepository.duplicateCheckByMailSuffix(mailSuffix, true, ownerUserIds, ownerDeptIds, isLeader));
        return customerIds;
    }

    @Override
    public List<CustomerCheckSettingQueryOutput> settingQuery() {
        return CustomerCheckConvert.INSTANCE.settingQueryOutputConvert(customerCheckSettingRepository.findAll());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean settingEdit(List<CustomerCheckSettingEditInput> inputVO) {
        List<String> columnIds = inputVO.stream().map(CustomerCheckSettingEditInput::getColumnName).collect(Collectors.toList());
        customerCheckSettingRepository.deleteByNotInColumnId(columnIds);
        for(CustomerCheckSettingEditInput input : inputVO ){
            if(customerCheckSettingRepository.findByColumnName(input.getColumnName()).isEmpty()){
                customerCheckSettingRepository.insert(CustomerCheckSettingDraft.$.produce(draft -> draft.setColumnName(input.getColumnName())));
            }
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean merge(CustomerCheckMergeInput inputVO) {
        CustomerInfo oldCustomer = customerInfoRepository.findByCustomerNo(inputVO.getCustomerNo()).orElse(null);
        if(oldCustomer == null){
            throw exception(CUSTOMER_INFO_NOT_EXIST);
        }
        CustomerInfo mainCustomer = CustomerCheckConvert.INSTANCE.mergeInputConvert(inputVO);
        mainCustomer = CustomerInfoDraft.$.produce(mainCustomer,
                draft -> draft.setId(oldCustomer.id()).setCountryId(draft.countryId() == null ? 0 : draft.countryId())
                        .setCityId(draft.cityId() == null ? 0 : draft.cityId())
                        .setDistrictId(draft.districtId() == null ? 0 : draft.districtId())
        );
        customerInfoRepository.update(mainCustomer);
        for(Long mergeCustomerId : inputVO.getIds()){
            if(mergeCustomerId == mainCustomer.id())
                continue;
            mergeImage(mergeCustomerId, mainCustomer.id());
            mergeWebsite(mergeCustomerId, mainCustomer.id());
            mergeContact(mergeCustomerId, mainCustomer.id());
        }
        List<Long> recycleIds = inputVO.getIds().stream().filter(id -> id != oldCustomer.id()).collect(Collectors.toList());
        customerInfoRepository.batchUpdateIsRecycle(recycleIds, true);
        return true;
    }

    private void mergeImage(Long mergeCustomerId, Long mainCustomerId){
        List<CustomerImage> mergeCustomerImages = customerImageRepository.findByCustomerId(mergeCustomerId);
        if(mergeCustomerImages.size() == 0)
            return;
        List<String> mailCustomerImageUrls = customerImageRepository.findByCustomerId(mainCustomerId).stream().map(CustomerImage::url).collect(Collectors.toList());
        for(CustomerImage image : mergeCustomerImages){
            if(!mailCustomerImageUrls.contains(image.url())){
                customerImageRepository.insert(CustomerImageDraft.$.produce(draft -> draft.setCustomerId(mainCustomerId).setUrl(image.url())));
            }
        }
    }

    private void mergeWebsite(Long mergeCustomerId, Long mainCustomerId){
        List<CustomerWebsite> mergeCustomerWebsite = customerWebsiteRepository.findByCustomerId(mergeCustomerId);
        if(mergeCustomerWebsite.size() == 0)
            return;
        List<String> mailCustomerWebsites = customerWebsiteRepository.findByCustomerId(mainCustomerId).stream().map(CustomerWebsite::website).collect(Collectors.toList());
        for(CustomerWebsite website : mergeCustomerWebsite){
            if(!mailCustomerWebsites.contains(website.website())){
                customerWebsiteRepository.insert(CustomerWebsiteDraft.$.produce(draft -> draft.setCustomerId(mainCustomerId).setWebsite(website.website())));
            }
        }

    }

    private void mergeContact(Long mergeCustomerId, Long mainCustomerId){
        List<CustomerContact> mergeCustomerContacts = customerContactRepository.findByCustomerId(mergeCustomerId);
        if(mergeCustomerContacts.size() == 0)
            return;
        for(CustomerContact contact : mergeCustomerContacts){
            CustomerContact newContact = customerContactRepository.insert(CustomerContactDraft.$.produce(contact, draft ->
            {
                draft.setCustomerId(mainCustomerId).setIsMain(false);
                DraftObjects.unload(draft, CustomerContactProps.ID);
            }));
            insertContactMail(contact.id(), newContact.id());
            insertContactBusinessCard(contact.id(), newContact.id());
            insertContactPhone(contact.id(), newContact.id());
            insertContactSocialMedia(contact.id(), newContact.id());
        }
    }

    private void insertContactMail(Long oldContactId, Long newContactId){
        List<CustomerContactMail> oldMails = customerContactMailRepository.findByContactId(oldContactId);
        if(oldMails.size() == 0)
            return;
        List<CustomerContactMail> newMails = new ArrayList<>();
        for(CustomerContactMail oldMail : oldMails){
            newMails.add(CustomerContactMailDraft.$.produce(oldMail, draft -> {
                draft.setContactId(newContactId);
                DraftObjects.unload(draft, CustomerContactMailProps.ID);
            }));
        }
        customerContactMailRepository.saveEntities(newMails, SaveMode.INSERT_ONLY);
    }

    private void insertContactBusinessCard(Long oldContactId, Long newContactId){
        List<CustomerContactBusinessCard> oldBusinessCards = customerContactBusinessCardRepository.findByContactId(oldContactId);
        if(oldBusinessCards.size() == 0)
            return;
        List<CustomerContactBusinessCard> newBusinessCards = new ArrayList<>();
        for(CustomerContactBusinessCard oldBusinessCard : oldBusinessCards){
            newBusinessCards.add(CustomerContactBusinessCardDraft.$.produce(oldBusinessCard, draft -> {
                draft.setContactId(newContactId);
                DraftObjects.unload(draft, CustomerContactBusinessCardProps.ID);
            }));
        }
        customerContactBusinessCardRepository.saveEntities(newBusinessCards, SaveMode.INSERT_ONLY);

    }

    private void insertContactPhone(Long oldContactId, Long newContactId){
        List<CustomerContactPhone> oldPhones = customerContactPhoneRepository.findByContactId(oldContactId);
        if(oldPhones.size() == 0)
            return;
        List<CustomerContactPhone> newPhones = new ArrayList<>();
        for(CustomerContactPhone oldPhone : oldPhones){
            newPhones.add(CustomerContactPhoneDraft.$.produce(oldPhone, draft -> {
                draft.setContactId(newContactId);
                DraftObjects.unload(draft, CustomerContactPhoneProps.ID);

            })) ;
        }
        customerContactPhoneRepository.saveEntities(newPhones, SaveMode.INSERT_ONLY);

    }

    private void insertContactSocialMedia(Long oldContactId, Long newContactId){
        List<CustomerContactSocialMedia> oldSocialMedias = customerContactSocialMediaRepository.findByContactId(oldContactId);
        if(oldSocialMedias.size() == 0)
            return;
        List<CustomerContactSocialMedia> newSocialMedias = new ArrayList<>();
        for(CustomerContactSocialMedia oldSocialMedia : oldSocialMedias){
            newSocialMedias.add(CustomerContactSocialMediaDraft.$.produce(oldSocialMedia, draft -> {
                draft.setContactId(newContactId);
                DraftObjects.unload(draft, CustomerContactSocialMediaProps.ID);
            }));
        }
        customerContactSocialMediaRepository.saveEntities(newSocialMedias, SaveMode.INSERT_ONLY);

    }

}
